package container

import (
	"io"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"syscall"

	"gitee.com/chuanjinge/go-container/container/info"
	"gitee.com/chuanjinge/go-container/option"
	"github.com/creack/pty"
	"golang.org/x/term"
)

// 在伪终端中运行指定命令

func runContainerWithPty(containerId string, newProc *exec.Cmd, option *option.CmdOption, writePipe *os.File, args []string, cInfo *info.ContainerInfo) error {
	// Create arbitrary command.
	// newProc := exec.Command("bash")

	// Start the command with a pty.
	ptmx, err := pty.Start(newProc)
	if err != nil {
		return err
	}
	// Make sure to close the pty at the end.
	defer func() { _ = ptmx.Close() }() // Best effort.

	log.Printf("[RunCmdAtPty]子进程ID: %d\n", newProc.Process.Pid)
	// 容器运行起来后的环境初始化
	if err := setContainerEnvAfterRun(containerId, newProc, option, writePipe, args, cInfo); err != nil {
		return err
	}

	if option.Daemon {

		// logfile1, _ := os.Stat(cInfo.LogPath)
		// log.Printf("here log file1 size: %d\n", logfile1.Size())

		// 守护进程模式下关闭主进程的输入和输出
		file, err := os.OpenFile("/dev/null", os.O_RDWR, 0)
		if err != nil {
			return err
		}
		log.Printf("[主]devnull的fd:%d\n", int(file.Fd()))
		log.Printf("[主]将标准输入重定向到 /dev/null \n")
		syscall.Dup2(int(file.Fd()), int(os.Stdin.Fd()))
		syscall.Dup2(int(file.Fd()), int(os.Stdout.Fd()))

		logfile, _ := os.Stat(cInfo.LogPath)
		log.Printf("here log file size: %d\n", logfile.Size())
	} else {
		// 前台进程模式下需要依赖标准输入和输出
		// Handle pty size.
		ch := make(chan os.Signal, 1)
		signal.Notify(ch, syscall.SIGWINCH)
		go func() {
			for range ch {
				if err := pty.InheritSize(os.Stdin, ptmx); err != nil {
					log.Printf("[RunCmdAtPty]error resizing pty: %s", err)
				}
			}
		}()
		ch <- syscall.SIGWINCH                        // Initial resize.
		defer func() { signal.Stop(ch); close(ch) }() // Cleanup signals when done.

		// Set stdin in raw mode.
		oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
		if err != nil {
			return err
		}
		defer func() { _ = term.Restore(int(os.Stdin.Fd()), oldState) }() // Best effort.

	}

	// @todo 一个疑问：为什么守护进程模式下重定向了标准输入和输出到 /dev/null ，但是此处的 io.Copy 仍然处于阻塞状态
	//                难道此处的 copy 的第一个 os.Stdout 不是指的主进程的标准输出？而是子进程的标准输出？
	// Copy stdin to the pty and the pty to stdout.
	// NOTE: The goroutine will keep reading until the next keystroke before returning.
	go func() { _, _ = io.Copy(ptmx, os.Stdin) }()
	_, _ = io.Copy(os.Stdout, ptmx)
	// 实际上copy的时候就阻塞在这里了，当我们退出bash的时候才执行下边的after copy...的输出
	log.Println("[RunCmdAtPty]after copy from pty...")

	return nil
}
